package com.wxit.limit.test;
import java.util.Arrays;
import java.util.Scanner;

public class cipandiaodu {
	 public static void main(String args[]) {
	        // 磁道按访问顺序初始化

	        //diskArr1 是为先来先服务准备的副本，diskArr是为方便后面 SSTF、SCAN、CSCN算法准备的，diskArr会被排序。
	        Integer diskArr1[] ={ 98,138, 37, 122, 14, 124, 65, 67};
	        Integer diskArr[] ={ 98,138, 37, 122, 14, 124, 65, 67};
	        System.out.println("磁盘访问请求序列初始化完成，请输入序号：");
	        int choice;
	        while(true) {
	            System.out.println("----------------------------");
	            System.out.println("1 调用先来先服务算法");
	            System.out.println("2 调用最短寻道时间优先算法");
	            System.out.println("3 调用扫描算法");
	            System.out.println("4 调用循环扫描算法");
	            System.out.println("----------------------------");
	            Scanner in = new Scanner(System.in);
	            choice = in.nextInt();
	            if (choice == 1) {
	                System.out.println("----------------------------");
	                System.out.println("先来先服务算法：");
	                FCFS(diskArr1);
	            } else if (choice == 2) {
	                System.out.println("----------------------------");
	                System.out.println("最短寻道时间优先算法：");
	                SSTF(diskArr);
	            } else if (choice == 3) {
	                System.out.println("----------------------------");
	                System.out.println("扫描算法：");
	                SCAN(diskArr);
	            }else if(choice ==4){
	                System.out.println("----------------------------");
	                System.out.println("循环扫描算法：");
	                CSCAN(diskArr);
	            }
	            System.out.println("输入任意字符继续");
	            in.next();

	        }

	    }
	    /**
	     * FCFS功能：先来先服务算法
	     * 参数含义：
	     *      diskArr：存放磁道号的数组
	     * */
	    public static void FCFS(Integer diskArr[]) {
	        Integer moveDistance[] = new Integer[diskArr.length]; 
	        // 记录移动距离

	        int readWriteHead = 53; 
	        // 标记读写头
	        for (int i = 0; i < diskArr.length; i++) {
	            if (readWriteHead < diskArr[i]) {
	                moveDistance[i] = diskArr[i] - readWriteHead;
	                readWriteHead = diskArr[i];
	            } else {
	                moveDistance[i] = readWriteHead - diskArr[i];
	                readWriteHead = diskArr[i];
	            }
	        }
	        System.out.println("磁盘访问顺序：");
	        printArray(diskArr);
	        System.out.println("磁头的移动距离：");
	        printArray(moveDistance);
	        calculateAverage(moveDistance);
	    }

	    /**
	     * SSTF功能：最短寻道时间优先算法
	     * 参数含义：
	     *      distArr：存放磁道号的数组
	     * */
	    public static void SSTF(Integer diskArr[]) {
	        Arrays.sort(diskArr);
	        Integer[] moveDistance = new Integer[diskArr.length]; // 记录移动距离
	        Integer[] orderhead = new Integer[diskArr.length];
	        int readWriteHead = 53; // 标记读写头
	        int indexL = 0;         // 用于记录左下标
	        int indexR = 0;         // 用于记录右下标
	        int numOrder = 0;       // 用于记录order下标
	        int situation = 0;      // 表示两种情况，0为出现相等，1为没出现相等
	        for (int i = 0; i < diskArr.length; i++) {
	            if (diskArr[i] == readWriteHead) {// 如果相等，那就直接先访问这个磁道号
	                // 这个磁道号的上下两个位置，必是一大一小
	                moveDistance[0] = 0;
	                indexL = i - 1;
	                indexR = i + 1;
	                break;
	            } else if (diskArr[i] > readWriteHead) {
	                // 如果没相等，那么找到第一个大于读写头的前一个位置和当前位置，必是一大一小
	                indexL = i - 1;
	                indexR = i;
	                situation = 1;
	                break;
	            }
	        }
	        while(true){
	            if(situation == 0){
	                numOrder = 1;  // order需要从1号位开始记录
	            }
	            while(indexL >= 0 && indexR < diskArr.length){
	                if(diskArr[indexR] - readWriteHead > readWriteHead - diskArr[indexL]){
	                    // 向减小方向移动
	                    moveDistance[numOrder] = readWriteHead - diskArr[indexL];
	                    orderhead[numOrder] = diskArr[indexL];
	                    readWriteHead = diskArr[indexL];
	                    indexL--; //-1
	                    numOrder++;
	                }else{
	                    // 向增大方向移动
	                    moveDistance[numOrder] = diskArr[indexR] - readWriteHead;
	                    orderhead[numOrder] = diskArr[indexR];
	                    readWriteHead = diskArr[indexR];
	                    indexR++;
	                    numOrder++;
	                }
	            }
	            // 跳出上一个循环，必然证明下一个循环一定是单方向
	            while(indexL >= 0){
	                // 如果左侧还有元素
	                moveDistance[numOrder] = readWriteHead - diskArr[indexL];
	                orderhead[numOrder] = diskArr[indexL];
	                readWriteHead = diskArr[indexL];
	                indexL--;
	                numOrder++;
	            }
	            while(indexR < diskArr.length){
	                // 如果右侧还有元素
	                moveDistance[numOrder] = diskArr[indexR] - readWriteHead;
	                orderhead[numOrder] = diskArr[indexR];
	                readWriteHead = diskArr[indexR];
	                indexR++;
	                numOrder++;
	            }
	            // 如果遍历过所有位置就跳出
	            break;
	        }

	        System.out.println("磁盘访问顺序：");
	        printArray(orderhead);
	        System.out.println("磁头的移动距离：");
	        printArray(moveDistance);
	        calculateAverage(moveDistance);
	    }

	    /**
	     * SCAN功能：扫描算法
	     * */
	    public static void SCAN(Integer[] diskArr) {
	    	
	        Arrays.sort(diskArr); //排序
	        Integer moveDistance[] = new Integer[diskArr.length]; // 记录移动距离
//	        Integer diskArr[] ={ 98,138, 37, 122, 14, 124, 65, 67};
	        Integer[] orderhead = new Integer[diskArr.length];

	        int readWriteHead = 53; // 标记读写头
	        int index = 0;          // 用于记录下标

	        for(int i=0;i<diskArr.length; i++){
	            if(diskArr[i] >= readWriteHead){
	                index=i;  //2
	                break;
	            }
	        }

	        int num = 0; // 用于记录order中下标的位置
	        // 先向增大方向
	        for (int k = index; k < diskArr.length; k++,num++) {
	            moveDistance[num] = Math.abs(diskArr[k] - readWriteHead);
	            readWriteHead = diskArr[k];
	            orderhead[num]=diskArr[k]; //记录磁道的访问顺序（记录磁道号）
	        }
	        // 再向减小方向
	        for (int j = index - 1; j >= 0; j--,num++) {
	            moveDistance[num] = readWriteHead - diskArr[j];
	            readWriteHead = diskArr[j];
	            orderhead[num]=diskArr[j];
	        }

	        System.out.println("磁盘访问顺序：");
	        printArray(orderhead);
	        System.out.println("磁头的移动距离：");
	        printArray(moveDistance);
	        calculateAverage(moveDistance);
	    }

	    /**
	     * CSCAN功能：循环扫描算法
	     * */
	    public static void CSCAN(Integer[] diskArr) {
	        Arrays.sort(diskArr);
	        Integer moveDistance[] = new Integer[diskArr.length]; // 记录移动距离
	        Integer orderhead[]=new Integer[diskArr.length];
//	        Integer diskArr[] ={ 98,138, 37, 122, 14, 124, 65, 67};
	        int readWriteHead = 53; // 标记读写头
	        int index = 0;          // 用于记录下标

	        for(int i=0;i<diskArr.length; i++){
	            if(diskArr[i] >= readWriteHead){
	                index=i;
	                break;
	            }
	        }
	        int num = 0; // 用于记录order中下标的位置
	        // 向磁道号递增方向扫描，扫描方向不变

	        while(num!=diskArr.length) {
	            if(index==diskArr.length){
	                index=0;
	            }
	            moveDistance[num] = Math.abs(diskArr[index] - readWriteHead);
	            readWriteHead = diskArr[index];
	            orderhead[num]=diskArr[index];
	            index++;
	            num++;
	        }

	        System.out.println("磁盘访问顺序：");
	        printArray(orderhead);
	        System.out.println("磁头的移动距离：");
	        printArray(moveDistance);
	        calculateAverage(moveDistance);
	    }

	    /**
	     * calculateAverage功能：计算平均移动道数 参数含义： moveDistance：记录移动距离的数组
	     * */
	    public static void calculateAverage(Integer moveDistance[]) {
	        double sum = 0; // 用于记录距离的总和
	        for (int i = 0; i < moveDistance.length; i++) {
	            sum += moveDistance[i];
	        }
	        System.out.println("该算法的平均移动道数为：" + (sum / moveDistance.length));
	    }


	    /**
	     * printArray功能：输出数组中的内容使用泛型。
	     * */
	    public static <E> void printArray(E[] list){
	        for (int i = 0; i < list.length; i++) {
	            System.out.print(list[i] + " ");
	        }
	        System.out.println();
	    }
	
}
